package com.fowo.api.sys.rd2.impl;

import com.baomidou.mybatisplus.annotation.TableName;
import com.fowo.api.common.annotaion.DataObjectType;
import com.fowo.api.sys.mapper.SysRoleData2Mapper;
import com.fowo.api.sys.rd2.RoleData2FilterService;
import com.fowo.api.sys.rd2.annotation.RoleData2;
import com.fowo.api.user.model.JwtUserInfo;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.PlainSelect;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * RoleData2 数据权限过滤服务实现
 * @author yl_ls
 */
@Service
public class RoleData2FilterServiceImpl implements RoleData2FilterService {
    private static final Map<Class<?>, Class<?>> SUPPORTS_MAP = new HashMap<>();

    @Resource
    private SysRoleData2Mapper roleData2Mapper;

    @Override
    public Class<?> supportForObject(Class<?> objectType) {
        if(SUPPORTS_MAP.containsKey(objectType)) {
            return SUPPORTS_MAP.get(objectType);
        }
        if (objectType.isAnnotationPresent(TableName.class)) {
            SUPPORTS_MAP.put(objectType, objectType);
            return objectType;
        }
        DataObjectType dataObjectType = objectType.getAnnotation(DataObjectType.class);
        if (dataObjectType != null) {
            SUPPORTS_MAP.put(objectType, dataObjectType.value());
            return dataObjectType.value();
        }
        SUPPORTS_MAP.put(objectType, null);
        return null;
    }

    @Override
    public void applyFilter(PlainSelect plainSelect, Long userId, Class<?> entityType) {
        RoleData2 roleData2 = entityType.getAnnotation(RoleData2.class);
        if (roleData2 == null) {
            // 没有找到此注解，不添加过滤
            return;
        }
        if (StringUtils.hasText(roleData2.funcKey()) && roleData2.groupId() > 0) {
            // 需要获取此用户是否在角色中对应模块包括 “全部可见”
            if(roleData2Mapper.selectExistsFuncAndUserId(roleData2.funcKey(), userId)) {
                // 全部可见，不添加过滤
                return;
            }
        }

        String joinTable = getCdauTableName(entityType);
        String joinAlias = "CDAU";

        Join join = new Join().withInner(true)
                .withRightItem(new Table(joinTable).withAlias(new Alias(joinAlias)))
                .addOnExpression(new EqualsTo(columnExp("t", "id"), columnExp(joinAlias, "`key`")));

        plainSelect.addJoins(join);
        addWhere(plainSelect, new EqualsTo(columnExp(joinAlias, "user_id"), new LongValue(userId)));
    }

    @Override
    public String appendInnerJoin(String objectName, String idColumn, String alias) {
        JwtUserInfo userInfo = JwtUserInfo.fromHeader();
        if (userInfo == null) {
            return "";
        }
        Long userId = userInfo.getUserId();
        String objectFullClassName = String.format("com.fowo.api.entity.%s", objectName);
        try {
            Class<?> entityType = Class.forName(objectFullClassName);
            RoleData2 roleData2 = entityType.getAnnotation(RoleData2.class);
            if (roleData2 != null && StringUtils.hasText(roleData2.funcKey()) && roleData2.groupId() > 0) {
                // 需要获取此用户是否在角色中对应模块包括 “全部可见”
                if(roleData2Mapper.selectExistsFuncAndUserId(roleData2.funcKey(), userId)) {
                    // 全部可见，不添加过滤
                    return "";
                }
            }
            String joinTable = getCdauTableName(entityType);

            Join join = new Join().withInner(true)
                    .withRightItem(new Table(joinTable).withAlias(new Alias(alias)))
                    .addOnExpression(new AndExpression()
                            .withLeftExpression(new EqualsTo(new Column(idColumn), columnExp(alias, "`key`")))
                            .withRightExpression(new EqualsTo(columnExp(alias, "user_id"), new LongValue(userId)))
                    );
            return join.toString();

        } catch (ClassNotFoundException e) {
            return "";
        }
    }

    private void addWhere(PlainSelect plainSelect, Expression expression) {
        Expression where = plainSelect.getWhere();
        if (where == null) {
            plainSelect.setWhere(expression);
            return;
        }
        Expression topWhere = new AndExpression().withLeftExpression(where).withRightExpression(expression);
        plainSelect.setWhere(topWhere);
    }

    private Expression columnExp(String tableAlias, String column) {
        return new Column().withColumnName(column).withTable(new Table().withAlias(new Alias(tableAlias)));
    }

    private String getCdauTableName(Class<?> entityType) {
        return String.format("CDAU_%s", entityType.getSimpleName());
    }
}
